#ifndef JSON_WRITER_H_INCLUDED
# define JSON_WRITER_H_INCLUDED

# include "value.h"
# include <vector>
# include <string>
# include <iostream>

namespace Json {

	class Value;

	/** \brief Abstract class for writers.
	*/
	class JSON_API Writer
	{
	public:
		virtual ~Writer();

		virtual std::string write( const Value &root ) = 0;
	};

	/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly).
	*
	* The JSON document is written in a single line. It is not intended for 'human' consumption,
	* but may be usefull to support feature such as RPC where bandwith is limited.
	* \sa Reader, Value
	*/
	class JSON_API FastWriter : public Writer
	{
	public:
		FastWriter();
		virtual ~FastWriter(){}

		void enableYAMLCompatibility();

	public: // overridden from Writer
		virtual std::string write( const Value &root );

	private:
		void writeValue( const Value &value );

		std::string document_;
		bool yamlCompatiblityEnabled_;
	};

	/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
	*
	* The rules for line break and indent are as follow:
	* - Object value:
	*     - if empty then print {} without indent and line break
	*     - if not empty the print '{', line break & indent, print one value per line
	*       and then unindent and line break and print '}'.
	* - Array value:
	*     - if empty then print [] without indent and line break
	*     - if the array contains no object value, empty array or some other value types,
	*       and all the values fit on one lines, then print the array on a single line.
	*     - otherwise, it the values do not fit on one line, or the array contains
	*       object or non empty array, then print one value per line.
	*
	* If the Value have comments then they are outputed according to their #CommentPlacement.
	*
	* \sa Reader, Value, Value::setComment()
	*/
	class JSON_API StyledWriter: public Writer
	{
	public:
		StyledWriter();
		virtual ~StyledWriter(){}

	public: // overridden from Writer
		/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
		* \param root Value to serialize.
		* \return String containing the JSON document that represents the root value.
		*/
		virtual std::string write( const Value &root );

	private:
		void writeValue( const Value &value );
		void writeArrayValue( const Value &value );
		bool isMultineArray( const Value &value );
		void pushValue( const std::string &value );
		void writeIndent();
		void writeWithIndent( const std::string &value );
		void indent();
		void unindent();
		void writeCommentBeforeValue( const Value &root );
		void writeCommentAfterValueOnSameLine( const Value &root );
		bool hasCommentForValue( const Value &value );
		static std::string normalizeEOL( const std::string &text );

		typedef std::vector<std::string> ChildValues;

		ChildValues childValues_;
		std::string document_;
		std::string indentString_;
		int rightMargin_;
		int indentSize_;
		bool addChildValues_;
	};

	/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
	to a stream rather than to a string.
	*
	* The rules for line break and indent are as follow:
	* - Object value:
	*     - if empty then print {} without indent and line break
	*     - if not empty the print '{', line break & indent, print one value per line
	*       and then unindent and line break and print '}'.
	* - Array value:
	*     - if empty then print [] without indent and line break
	*     - if the array contains no object value, empty array or some other value types,
	*       and all the values fit on one lines, then print the array on a single line.
	*     - otherwise, it the values do not fit on one line, or the array contains
	*       object or non empty array, then print one value per line.
	*
	* If the Value have comments then they are outputed according to their #CommentPlacement.
	*
	* \param indentation Each level will be indented by this amount extra.
	* \sa Reader, Value, Value::setComment()
	*/
	class JSON_API StyledStreamWriter
	{
	public:
		StyledStreamWriter( std::string indentation="\t" );
		~StyledStreamWriter(){}

	public:
		/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
		* \param out Stream to write to. (Can be ostringstream, e.g.)
		* \param root Value to serialize.
		* \note There is no point in deriving from Writer, since write() should not return a value.
		*/
		void write( std::ostream &out, const Value &root );

	private:
		void writeValue( const Value &value );
		void writeArrayValue( const Value &value );
		bool isMultineArray( const Value &value );
		void pushValue( const std::string &value );
		void writeIndent();
		void writeWithIndent( const std::string &value );
		void indent();
		void unindent();
		void writeCommentBeforeValue( const Value &root );
		void writeCommentAfterValueOnSameLine( const Value &root );
		bool hasCommentForValue( const Value &value );
		static std::string normalizeEOL( const std::string &text );

		typedef std::vector<std::string> ChildValues;

		ChildValues childValues_;
		std::ostream* document_;
		std::string indentString_;
		int rightMargin_;
		std::string indentation_;
		bool addChildValues_;
	};

	std::string JSON_API valueToString( Value::Int value );
	std::string JSON_API valueToString( Value::UInt value );
	std::string JSON_API valueToString( double value );
	std::string JSON_API valueToString( bool value );
	std::string JSON_API valueToQuotedString( const char *value );

	/// \brief Output using the StyledStreamWriter.
	/// \see Json::operator>>()
	std::ostream& operator<<( std::ostream&, const Value &root );

} // namespace Json



#endif // JSON_WRITER_H_INCLUDED
